from flask import Blueprint, request
from entity.User import User
from entity.Certification import Certification
from entity.Message import Message
from entity.Response import success,error,authority
from util.PasswordEncoder import encrypt_password,decrypt_password
from db import db
import jwt
import datetime
import random
from expiringdict import ExpiringDict

loginController = Blueprint('loginController',__name__)
jwt_secret_key = 'SagisawaArisu'

# 创建一个带过期时间的字典，过期时间为5分钟
cache = ExpiringDict(max_len=100, max_age_seconds=5 * 60)

"""
用户注册
input:
form:{
    username
    password
}
output:
result
"""
@loginController.route('/register',methods=['POST'])
def register():
    username = request.get_json().get('username')
    password = request.get_json().get('password')

    phone = request.get_json().get('phone')
    code = request.get_json().get('code')

    # 查询username是否重复
    user = User.query.filter_by(username=username).first()
    if user:
        return error(message='注册失败，用户名已存在！')
    # 查询手机号是否重复
    user = User.query.filter_by(phone=phone).first()
    if user:
        return error(message='注册失败，手机号已存在!')
    # 校验验证码
    if code != cache.get('code'):
        return error(message='注册失败，验证码错误!')
    # 给password加密
    encodedPassword = encrypt_password(password)
    # 存数据库
    user = User(username=username,password=encodedPassword,phone=phone)
    db.session.add(user)
    db.session.commit()
    # 生成jwt 设置有效时间为5分钟
    payload = {
        'username': username,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5)
    }
    token = jwt.encode(payload, jwt_secret_key, algorithm='HS256')
    return success(message='注册成功！',data=token)

"""
用户登录
input:
form:{
    username
    password
}
output:
result
"""
@loginController.route('/login',methods=['POST'])
def login():
    username = request.get_json().get('username')
    password = request.get_json().get('password')

    phone = request.get_json().get('phone')
    code = request.get_json().get('code')

    # 如果用户名不为空 就是账号密码登录
    if username != None and len(username) > 0:
        # 查询出对应用户
        user = User.query.filter_by(username=username).first()
        if user == None:
            return error(message='登陆失败，当前用户不存在！')
        # 对数据库中的密码解密
        decodedPassword = decrypt_password(user.password)
        if password != decodedPassword:
            return error(message='登陆失败，输入密码错误！')
    else: #否则就是手机号登录
        # 查询出对应哪个用户
        user = User.query.filter_by(phone=phone).first()
        if user == None:
            return error(message='登陆失败，当前手机号未注册用户')
        # 校验验证码
        if code != cache.get('code'):
            return error(message='验证码错误，请重新输入')

    # 生成jwt 设置有效时间为5分钟
    payload = {
        'username': user.username,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1)
    }
    token = jwt.encode(payload, jwt_secret_key, algorithm='HS256')
    return success(message='登陆成功！',data=token)

"""
根据token获取当前用户信息
input:
none:
output:
userinfo
"""
@loginController.route('/getNowUserInfo',methods=['GET'])
def getNowUserInfo():
    token = request.cookies.get('token')
    if not token:
        token = request.headers.get('token')
    if not token:
        return authority(message='鉴权失败，请先登录！')
    try:
        decoded_payload = jwt.decode(token, jwt_secret_key, algorithms=['HS256'])
        user = User.query.filter_by(username=decoded_payload['username']).first()
        return success(message='token解析成功',data=user.to_dict())
    except jwt.ExpiredSignatureError:
        return authority(message='token已过期，请重新登录！')
    except jwt.InvalidTokenError:
        return authority(message='无效token')

"""
修改密码
input:
新密码
output:
none
"""
@loginController.route('/updateInfo',methods=['PUT'])
def updateInfo():
    username = request.get_json().get('username')
    newPassword = request.get_json().get('newPassword')
    avatar = request.get_json().get('avatar')
    hospital = request.get_json().get('hospital')
    proof = request.get_json().get('proof')

    # 查询出对应用户
    user = User.query.filter_by(username=username).first()
    if user == None:
        return error(message='修改失败，当前用户不存在！')

    # 如果密码不为空
    if newPassword != None and len(newPassword) > 0:
        # 对新密码进行加密
        encodedPassword = encrypt_password(newPassword)
        User.query.filter_by(username=username).update({'password': encodedPassword})

    # 如果头像不为空
    if avatar != None and len(avatar) > 0:
        User.query.filter_by(username=username).update({'avatar': avatar})

    # 如果工作地点不为空 新增一条认证记录
    if hospital != None and len(hospital) > 0:
        certification = Certification(username,hospital,proof)
        db.session.add(certification)

    # 提交会话
    db.session.commit()
    return success(message='信息修改成功')

"""
判断token是否过期
input:
token
output:
result
"""
@loginController.route('/checkToken',methods=['GET'])
def checkToken():
    # 获取token
    token = request.cookies.get('token')
    if not token:
        token = request.headers.get('token')
    if not token:
        return authority(message='鉴权失败，请先登录！')

    # 校验token
    try:
        decoded_payload = jwt.decode(token, jwt_secret_key, algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
        return authority(message='token已过期，请重新登录！')
    except jwt.InvalidTokenError:
        return authority(message='无效token')
    return success()

"""
获取手机短信验证码
input:
phone number
output:
code
"""
@loginController.route('/getPhoneCode',methods=['GET'])
def getPhoneCode():
    phone = request.args.get('phone')
    print(phone)
    # 随机生成4位数字    
    random_number = random.randint(1000, 9999)
    access_key_id = 'LTAI5tSXK6NrNBRRKMREWogX'
    access_key_secret = 'VtNmUW6TZt1oJpYskBizw6u0p8bUXw'
    sms_client = SmsClient(access_key_id, access_key_secret)
    # todo 取消注释
    result = sms_client.send_sms(phone, str(random_number))
    print(str(random_number))
    if result:
        # 存入cache中
        global cache
        cache['code'] = str(random_number)
        return success(message='短信发送成功')
    else:
        return error(message='短信发送失败')
    
"""
获取所有待申请的数据
input:
none
output:
list
"""
@loginController.route('/getAllIdentityData',methods=['GET'])
def getAllIdentityData():
    certificationList = Certification.query.filter(Certification.status != 1).all()
    dataList = []
    for certification in certificationList:
        dataList.append(certification.to_dict())
    return success(message='获取待审核列表成功',data=dataList)

"""
处理认证信息
input:
certification form
output:
none
"""
@loginController.route('/dealIdentity',methods=['PUT'])
def dealIdentity():
    certificationId = request.get_json().get('certificationId')
    username = request.get_json().get('username')
    status = request.get_json().get('status')
    reason = request.get_json().get('reason')

    token = request.cookies.get('token')
    if not token:
        token = request.headers.get('token')

    decoded_payload = jwt.decode(token, jwt_secret_key, algorithms=['HS256'])
    fromUsername = decoded_payload['username']

    certification = Certification.query.filter_by(id=certificationId).first()
    if certification == None:
        return error(message='认证信息不存在')

    # 通过
    if status == 0:
        Certification.query.filter_by(id=certificationId).update({'status': 1})
        # 更新用户信息
        User.query.filter_by(username=username).update({'hospital': certification.hospital,'role': 1})
        # 留言 告知信息已通过
        message = Message(from_username=fromUsername,to_username=username,file_url=None,content='您的医生身份认证已通过。')
        db.session.add(message)
    # 不通过
    if status == 1:
        Certification.query.filter_by(id=certificationId).update({'status': 2})
        # 给对方留言 告知不通过原因
        message = Message(from_username=fromUsername,to_username=username,file_url=None,content=reason)
        db.session.add(message)
    db.session.commit()

    return success(message='认证信息处理成功')

"""
获取所有用户
input:
none
output:
user list
"""
@loginController.route('/getUserList',methods=['GET'])
def getUserList():
    userList = User.query.filter(User.role != 2).all()
    dataList = []
    for user in userList:
        dataList.append(user.to_dict())
    return success(message='获取用户列表成功',data=dataList)

"""
管理员更新指定用户的身份
input:
role
output:
none
"""
@loginController.route('/updateUserRole',methods=['PUT'])
def updateUserRole():
    role = request.args.get('role')
    username = request.args.get('username')

    user = User.query.filter_by(username=username).first()
    if user == None:
        return error(message='用户不存在')

    User.query.filter_by(username=username).update({'role': role})
    db.session.commit()
    return success(message='更新成功')

"""
删除指定用户
input:
username
output:
onne
"""
@loginController.route('/deleteUser',methods=['DELETE'])
def deleteUser():
    username = request.args.get('username')

    user = User.query.filter_by(username=username).first()
    if user == None:
        return error(message='用户不存在')
    
    db.session.delete(user)
    db.session.commit()
    return success(message='删除用户成功')

# 解析token获取用户名
def getUsername():
    # 获取token
    token = request.cookies.get('token')
    if not token:
        token = request.headers.get('token')

    # 校验token
    decoded_payload = jwt.decode(token, jwt_secret_key, algorithms=['HS256'])
    return decoded_payload['username']


# 使用阿里云短信服务发送短信验证码
from alibabacloud_dysmsapi20170525.client import Client as Dysmsapi20170525Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_dysmsapi20170525 import models as dysmsapi_20170525_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
 
 
class SmsClient:
    def __init__(self, access_key_id: str, access_key_secret: str, endpoint: str = 'dysmsapi.aliyuncs.com'):
        """
        初始化阿里云短信客户端
        :param access_key_id: 阿里云AccessKey ID
        :param access_key_secret: 阿里云AccessKey Secret
        :param endpoint: API服务端点，默认为'dysmsapi.aliyuncs.com'
        """
        self.access_key_id = access_key_id
        self.access_key_secret = access_key_secret
        self.endpoint = endpoint
 
    def create_client(self) -> Dysmsapi20170525Client:
        """
        使用AccessKey初始化账号Client
        :return: Dysmsapi20170525Client实例
        """
        config = open_api_models.Config(
            access_key_id=self.access_key_id,
            access_key_secret=self.access_key_secret
        )
        config.endpoint = self.endpoint
        return Dysmsapi20170525Client(config)
 
    def send_sms(self, phone_number: str, code: str) -> bool:
        """
        发送短信
        :param phone_number: 接收短信的手机号（多个号码以逗号分隔）
        :param code: 验证码
        :return: 返回是否成功的布尔值
        """
        client = self.create_client()
        send_sms_request = dysmsapi_20170525_models.SendSmsRequest(
            phone_numbers=phone_number,
            sign_name='龙猫文化',
            template_code='SMS_474835449',
            template_param=f'{{"code":"{code}"}}'
        )
        try:
            # 发送短信请求
            response = client.send_sms_with_options(send_sms_request, util_models.RuntimeOptions())
            print(response.status_code)
            print(f"Response Body: {response.body}")
            response_dict = vars(response.body)
            code = response_dict.get('code')
            message = response_dict.get('message')
            # 判断响应的状态
            if response.status_code == 200 and message == 'OK' and code == 'OK':
                print("短信发送成功")
                return True  # 短信发送成功
            else:
                print(f"短信发送失败，错误码: {response_dict.get('code')}")
                return False  # 短信发送失败
 
        except Exception as error:
            # 错误处理
            print(f"发送短信失败: {error.message}")
            print(f"建议操作: {error.data.get('Recommend')}")
            UtilClient.assert_as_string(error.message)
            return False  # 发生异常时返回失败